package com.sifou.courses.service.impl;

import com.sifou.courses.dto.SeckillGoodsDto;
import com.sifou.courses.dto.SeckillOrderDto;
import com.sifou.courses.event.TradeFailureEvent;
import com.sifou.courses.exception.CustomException;
import com.sifou.courses.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.RetryException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @author liuzhongxu
 * @date 2020/8/18
 */
@SuppressWarnings("AlibabaRemoveCommentedCode")
@Slf4j
@Service
public class TradeServiceImpl implements ITradeService, ITradeCallBackService {

    @Resource(name = "seckillService")
    private ISeckillService seckillService;

    @Resource(name = "seckillOptimisticLockService")
    private ISeckillService seckillOptimisticLockService;

    @Resource(name = "seckillOptimisticLockRedisSafeService")
    private ISeckillService seckillOptimisticLockRedisSafeService;

    @Resource(name = "seckillRedisService")
    private ISeckillService seckillRedisService;

    @Resource
    private IOrderService orderService;

    @Resource
    private IGoodsService goodsService;

    private void pay(ISeckillService seckillService, Integer orderId) {
        if (orderId % 2 == 0) {
            ITradeCallBackService.paySuccess.offer(orderId);
            log.debug("支付成功回调");
        } else {
            TradeFailureEvent failureEvent = new TradeFailureEvent();
            failureEvent.setOrderId(orderId);
            failureEvent.setSeckillService(seckillService);
            ITradeCallBackService.payFailure.offer(failureEvent);
            log.debug("支付失败回调");
        }
    }

//    @Transactional(rollbackFor = Exception.class)
    Integer doTrade(ISeckillService seckillService, Integer id) {
        log.debug("doTrade goodsId={}", id);
        // 检查库存
        SeckillGoodsDto goodsDto = seckillService.checkStock(id);
        // 扣库存
        Integer sale = seckillService.saleStock(goodsDto);
        if (sale <= 0) {
            throw new CustomException("扣库存失败");
        }
        // 下订单
        Integer orderId = orderService.createOrder(goodsDto);

        // 模拟支付
        pay(seckillService, orderId);
        return orderId;
    }

    @Override
    public Integer doCallBackSuccess(SeckillOrderDto orderDto) {
        // 改订单状态
        return orderService.updateOrderStatus(orderDto.getId(), 1);
    }

    @Retryable(value = RetryException.class, backoff = @Backoff(delay = 500L))
    @Override
    public Integer doCallBackFailure(ISeckillService seckillService, SeckillOrderDto orderDto) {
        // 回滚库存
        seckillService.rollBackStock(orderDto);
        // 关闭订单
        return orderService.updateOrderStatus(orderDto.getId(), 2);
    }

    @Recover
    @Override
    public Integer doCallBackRecover(RetryException e) {
        // 报警，日志，流水
        log.error("doCallBackRecover msg={}", e.getMessage());
        return null;
    }

    @Override
    public Integer doWrongTrade(Integer id) {
        return doTrade(seckillService, id);
    }

    @Override
    public Integer doOptimisticLockTrade(Integer id) {
        return doTrade(seckillOptimisticLockService, id);
    }

    @Override
    public Integer doOptimisticLockTradeWithRedis(Integer id) {
        return doTrade(seckillOptimisticLockRedisSafeService, id);
    }

    @Override
    public Integer doTradeWithRedis(Integer id) {
        return doTrade(seckillRedisService, id);
    }
}
